home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
Pakiet bezpieczenstwa
/
mini Pentoo LiveCD 2006.1
/
mpentoo-2006.1.iso
/
livecd.squashfs
/
usr
/
lib
/
metasploit
/
msfpescan
< prev
next >
Wrap
Text File
|
2006-06-30
|
12KB
|
520 lines
#!/usr/bin/perl
###############
##
# Name: msfpescan
# Author: H D Moore <hdm [at] metasploit.com>
# Version: $Revision: 1.34 $
# Description: Search PE files for given opcodes
# License:
#
# This file is part of the Metasploit Exploit Framework
# and is subject to the same licenses and copyrights as
# the rest of this package.
#
##
require 5.6.0;
use FindBin qw{$RealBin};
use lib "$RealBin/lib";
use Getopt::Std;
use strict;
use Pex::PEInfo;
use Pex;
use Pex::Nasm::Ndisasm;
use Msf::ColPrint;
use Msf::TextUI;
no utf8;
no locale;
Msf::UI::ActiveStateSucks();
Msf::UI::BrokenUTF8();
my $VERSION = '$Revision: 1.34 $';
my %opts = ();
my %jmps =
(
"\xff\xd0" => ["eax", "call"],
"\xff\xe0" => ["eax", "jmp" ],
"\xff\xd1" => ["ecx", "call"],
"\xff\xe1" => ["ecx", "jmp" ],
"\xff\xd2" => ["edx", "call"],
"\xff\xe2" => ["edx", "jmp" ],
"\xff\xd3" => ["ebx", "call"],
"\xff\xe3" => ["ebx", "jmp" ],
"\xff\xe4" => ["esp", "jmp" ],
"\xff\xd5" => ["ebp", "call"],
"\xff\xe5" => ["ebp", "jmp" ],
"\xff\xd6" => ["esi", "call"],
"\xff\xe6" => ["esi", "jmp" ],
"\xff\xd7" => ["edi", "call"],
"\xff\xe7" => ["edi", "jmp" ],
"\x50\xc3" => ["eax", "push"],
"\x53\xc3" => ["ebx", "push"],
"\x51\xc3" => ["ecx", "push"],
"\x52\xc3" => ["edx", "push"],
"\x54\xc3" => ["esp", "push"],
"\x55\xc3" => ["ebp", "push"],
"\x56\xc3" => ["esi", "push"],
"\x57\xc3" => ["edi", "push"],
);
my %pops =
(
"eax" => "\x58",
"ebx" => "\x5b",
"ecx" => "\x59",
"edx" => "\x5a",
"esi" => "\x5e",
"edi" => "\x5f",
"ebp" => "\x5d",
);
getopts("f:d:j:sx:a:B:A:I:nhvDES", \%opts);
Usage() if($opts{'h'});
Version() if($opts{'v'});
if ($opts{'h'} ||
(! defined($opts{'f'}) && ! defined($opts{'d'})) ||
(! defined($opts{'j'}) &&
! defined($opts{'x'}) &&
! defined($opts{'a'}) &&
! defined($opts{'D'}) &&
! defined($opts{'S'}) &&
! $opts{'s'})
)
{
Usage();
exit(0);
}
my $func;
my $args = { };
if(exists($opts{'s'})) {
$func = \&popPopRet;
}
elsif(exists($opts{'j'})) {
$func = \&jmpReg;
$args->{'reg'} = $opts{'j'};
}
elsif(exists($opts{'x'})) {
$func = \®ex;
$args->{'regex'} = $opts{'x'};
}
elsif(exists($opts{'a'})) {
$func = \&address;
$args->{'address'} = hex($opts{'a'});
}
elsif(exists($opts{'D'})) {
$func = \&dumpinfo;
$args->{'dumpinfo'} = hex($opts{'D'});
}
elsif(exists($opts{'S'})) {
$func = \&identify;
$args->{'identify'} = hex($opts{'S'});
}
$args->{'before'} = $opts{'B'} if(exists($opts{'B'}));
$args->{'after'} = $opts{'A'} if(exists($opts{'A'}));
if($opts{'f'}) {
my $filename = $opts{'f'};
my $pe = Pex::PEInfo->new('File' => $filename, 'Debug' => $opts{'E'}, 'FullResources' => 1);
if (! $pe)
{
print STDERR "$0: could not load PE image from file.\n";
exit(0);
}
if ($opts{'I'}) { $pe->ImageBase($opts{'I'}) }
&{$func}($pe, $args);
}
else {
my $dir = $opts{'d'};
opendir(INDIR, $dir);
my @files = readdir(INDIR);
closedir(INDIR);
foreach my $file (@files) {
if($file =~ /^(.{8})\.rng/) {
#print "Good file: $dir $file\n";
my $pe = SkapeFoo->new($dir . '/' . $file, hex($1));
&{$func}($pe, $args);
}
}
}
# Identify the binary based on signatures
sub identify {
my $pe = shift;
my $data = $pe->Raw;
my $args = shift;
my $sigf = $RealBin.'/data/msfpescan/identify.txt';
my %sigs = ();
my $ep = $pe->VirtualToOffset($pe->EntryPoint + $pe->ImageBase);
my $filename = $pe->Filename;
if(! open(SIGS, "<" .$sigf)) {
print STDERR "[*] Could not load the signature database: $!\n";
return;
}
# If ep_only is set, only scan from the endpoint offset
my $edata = substr($data, $ep, 8192);
my $name;
my $regx;
my $ep_only = 0;
my $ep_off = 0;
my $sidx = 0;
# If the entrypoint was mangled, disable ep scanning
if (! length($edata)) {
$ep_off = 1;
}
while (my $line = <SIGS>) {
next if $line =~ /^\s*#/;
if ($line =~ m/\[(.*)\]/) {
if ($name) {
$sigs{$name} = [$regx, $ep_only];
}
$name = $1." [".$sidx++."]";
$ep_only = 0;
next;
}
if ($line =~ m/signature\s*=\s*(.*)\s*/i) {
my $pattern = $1;
$regx = '';
foreach my $c (split(/\s+/, $pattern)) {
if ($c eq '??') {
$regx .= '.';
}else{
$regx .= "\\x".$c;
}
}
}
if ($line =~ m/ep_only\s*=\s*T.*\s*/i) {
$ep_only = 1 if ! $ep_off;
}
}
if ($name) {
$sigs{$name} = [$regx, $ep_only];
}
foreach my $sig (keys %sigs) {
my $regex = $sigs{$sig}->[0];
my @addrs= ();
# Only compare the entry point?
if ($sigs{$sig}->[1]) {
if ($edata =~ /^($regex)/) {
push @addrs, $pe->EntryPoint + $pe->ImageBase;
}
} else {
while($data =~ m/($regex)/g) {
my $found = $1;
my $index = pos($data) - length($found);
my $va = $pe->OffsetToVirtual($index);
push @addrs, $va if $va;
}
}
if (@addrs) {
print "$filename: $sig (".scalar(@addrs)." matches)\n";
}
}
}
# Scan for pop/pop/ret addresses
sub popPopRet
{
my $pe = shift;
my $data = $pe->Raw;
my $args = shift;
foreach my $rA (keys(%pops))
{
foreach my $rB (keys(%pops))
{
my $opc = $pops{$rA} . $pops{$rB} . "\xc3";
my $lst = 0;
my $idx = index($data, $opc, $lst);
while ($idx > 0)
{
my $va = $pe->OffsetToVirtual($idx);
printf("0x%.8x $rA $rB ret\n", $va) if $va;
$lst = $idx + 1;
$idx = index($data, $opc, $lst);
}
}
}
}
# Scan for jmp/call/push,ret addresses
sub jmpReg
{
my $pe = shift;
my $data = $pe->Raw;
my $args = shift;
my $reg = $args->{'reg'};
foreach my $opc (keys(%jmps))
{
next if ($reg && lc($reg) ne $jmps{$opc}->[0]);
my $lst = 0;
my $idx = index($data, $opc, $lst);
while ($idx > 0)
{
my ($reg, $typ) = @{$jmps{$opc}};
my $va = $pe->OffsetToVirtual($idx);
printf("0x%.8x $typ $reg\n", $va) if $va;
$lst = $idx + 1;
$idx = index($data, $opc, $lst);
}
}
}
# Regex
sub regex {
my $pe = shift;
my $data = $pe->Raw;
my $args = shift;
my $regex = $args->{'regex'};
$regex .= '.' x $args->{'after'} if($args->{'after'});
$regex = ('.' x $args->{'before'}) . $regex if($args->{'before'});
while($data =~ m/($regex)/g) {
my $found = $1;
my $index = pos($data) - length($found);
my $va = $pe->OffsetToVirtual($index);
printf("0x%.8x %s\n", $va, hexOutput($found)) if $va;
}
}
sub address {
my $pe = shift;
my $data = $pe->Raw;
my $args = shift;
my $address = $args->{'address'} - $args->{'before'};
my $length = $args->{'before'} + $args->{'after'};
$length = 1 if(!$length);
my $index = $pe->VirtualToOffset($address);
my $found = substr($data, $index, $length);
return if(!defined($index) || length($found) == 0);
printf("0x%.8x %s\n", $address, hexOutput($found));
}
sub dumpinfo {
my $pe = shift;
my $args = shift;
my @img_hdrs = $pe->ImageHeaders;
my @opt_img_hdrs = $pe->OptImageHeaders;
my $imports = $pe->Imports;
my $exports = $pe->Exports;
my $resources = $pe->Resources;
my $version = $pe->VersionStrings;
my $col;
my %setUEF = ();
my $setUEF_IAT = 0;
print "\n\n[ Image Headers ]\n\n";
$col = Msf::ColPrint->new(4, 4);
foreach my $hdr (@img_hdrs) {
$col->AddRow($hdr, sprintf("0x%.8x",$pe->ImageHeader($hdr)));
}
print $col->GetOutput;
print "\n\n[ Optional Headers ]\n\n";
$col = Msf::ColPrint->new(4, 4);
foreach my $hdr (@opt_img_hdrs) {
$col->AddRow($hdr, sprintf("0x%.8x",$pe->OptImageHeader($hdr)));
}
print $col->GetOutput;
print "\n\n[ Exported Functions ]\n\n";
$col = Msf::ColPrint->new(4, 4);
foreach my $name (@{ $exports->{'ordinals'} }) {
my $add = $exports->{'funcs'}->{$name}->{'add'};
my $ord = $exports->{'funcs'}->{$name}->{'ord'};
next if ! $ord;
$col->AddRow($ord, $name, sprintf("0x%.8x",$add));
}
print $col->GetOutput;
print "\n\n[ Imported Functions ]\n\n";
$col = Msf::ColPrint->new(4, 4);
foreach my $module (keys(%{ $imports })) {
foreach my $func (sort(keys(%{ $imports->{$module} }))) {
$col->AddRow($module, $func,
"IAT ".sprintf("0x%.8x", $imports->{$module}->{$func}->{'iat'})
);
if (lc($func) eq 'setunhandledexceptionfilter') {
$setUEF_IAT = $imports->{$module}->{$func}->{'iat'};
}
}
$col->AddRow("", "", "");
}
print $col->GetOutput;
print "\n[ Resources ]\n\n";
$col = Msf::ColPrint->new(4, 4);
foreach my $type (sort(keys(%{ $resources->{'Types'} }))) {
foreach my $name (sort(keys(%{ $resources->{'Types'}->{$type} }))) {
my $entry = $resources->{'Entries'}->{$name};
$col->AddRow($name, $entry->{'Name'}, "CP ".$entry->{'Code'}, $entry->{'Size'}." bytes");
}
}
print $col->GetOutput;
print "\n\n[ Version Strings ]\n\n";
$col = Msf::ColPrint->new(4, 4);
foreach my $lang (keys(%{ $version })) {
foreach my $name (sort(keys(%{ $version->{$lang} }))) {
$col->AddRow($lang, $name, $version->{$lang}->{$name});
}
}
print $col->GetOutput;
return if not $setUEF_IAT;
print "\n\n[ SetUnhandledExceptionFilter ]\n\n";
$col = Msf::ColPrint->new(4, 4);
my $data = $pe->Raw;
my $regex = "(\x68|\xff\x15)".pack("V", $setUEF_IAT);
$regex .= '.' x $args->{'after'} if($args->{'after'});
$regex = ('.' x $args->{'before'}) . $regex if($args->{'before'});
while($data =~ m/($regex)/g) {
my $found = $1;
my $index = pos($data) - length($found);
my $va = $pe->OffsetToVirtual($index);
if ($va) {
$col->AddRow(sprintf("0x%.8x", $va), hexOutput($found));
}
}
print $col->GetOutput;
}
sub hexOutput {
my $data = shift;
my $string = unpack('H*', $data);
if($opts{'n'}) {
# my $tempString = $string;
# $tempString =~ s/(..)/\\x$1/g;
$string .= "\n--- ndisasm output ---\n";
# $string .= `echo -ne "$tempString" | ndisasm -u /dev/stdin`;
$string .= Pex::Nasm::Ndisasm->DisasData($data);
$string .= "--- ndisasm output ---";
}
return($string);
}
sub Usage
{
print STDERR
qq{ Usage: $0 <input> <mode> <options>
Inputs:
-f <file> Read in PE file
-d <dir> Process memdump output
Modes:
-j <reg> Search for jump equivalent instructions
-s Search for pop+pop+ret combinations
-x <regex> Search for regex match
-a <address> Show code at specified virtual address
-D Display detailed PE information
-S Attempt to identify the packer/compiler
Options:
-A <count> Number of bytes to show after match
-B <count> Number of bytes to show before match
-I address Specify an alternate ImageBase
-n Print disassembly of matched data
};
exit(0);
}
sub Version {
my $ver = Pex::Utils::Rev2Ver($VERSION);
print STDERR qq{
Msfpescan Version: $ver
};
exit(0);
}
package SkapeFoo;
use strict;
sub new {
my $class = shift;
my $self = bless({ }, $class);
$self->Filename(shift);
$self->Base(shift);
$self->ReadInRaw;
return($self);
}
sub Filename {
my $self = shift;
$self->{'Filename'} = shift if(@_);
return($self->{'Filename'});
}
sub Base {
my $self = shift;
$self->{'Base'} = shift if(@_);
return($self->{'Base'});
}
sub Raw {
my $self = shift;
$self->{'Raw'} = shift if(@_);
return($self->{'Raw'});
}
sub ReadInRaw {
my $self = shift;
open(INFILE, '<' . $self->Filename) or return(0);
local $/;
my $data = <INFILE>;
close(INFILE);
$self->Raw($data);
return(1);
}
sub VirtualToOffset {
my $self = shift;
my $virtual = shift;
return($virtual - $self->Base);
}
sub OffsetToVirtual {
my $self = shift;
my $offset = shift;
return($offset + $self->Base);
}
1;